import type { DefaultOptionType } from 'ant-design-vue/lib/select'
import type { SortableOptions } from 'sortablejs'
import type { AutoScrollOptions } from 'sortablejs/plugins'

export const modalSizes = {
  xs: {
    width: 'min(calc(100vw - 32px), 448px)',
    height: 'min(90vh, 448px)',
  },
  sm: {
    width: 'min(calc(100vw - 32px), 640px)',
    height: 'min(90vh, 424px)',
  },
  md: {
    width: 'min(80vw, 900px)',
    height: 'min(90vh, 540px)',
  },
  lg: {
    width: 'min(80vw, 1280px)',
    height: 'min(90vh, 864px)',
  },
  fullscreen: {
    width: '100vw',
    height: '100vh',
  },
}

/**
 * Creates a promise that resolves after a specified delay.
 *
 * @param ms - The delay in milliseconds.
 * @returns A promise that resolves after the specified delay.
 *
 * @example
 * ```ts
 * // Wait for 2 seconds
 * await delay(2000);
 * console.log('2 seconds have passed');
 * ```
 */
export const ncDelay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

/**
 * Generates an array of a given length with content generated by the provided callback function.
 *
 * @param length - The length of the array to be created.
 * @param contentCallback - Optional function to generate content for each index. Defaults to using the index as content.
 * @returns The generated array with content.
 *
 * @example
 * // Generate an array of length 5 with default content
 * const array = ncArrayFrom(5);
 * console.log(array); // ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5']
 *
 * @example
 * // Generate an array of length 3 with custom content
 * const customArray = ncArrayFrom(3, (i) => `Custom Content ${i}`);
 * console.log(customArray); // ['Custom Content 0', 'Custom Content 1', 'Custom Content 2']
 */
export const ncArrayFrom = <T>(
  length: number,
  contentCallback: (i: number) => T = (i) => `Item ${i + 1}` as unknown as T,
): T[] => {
  return Array.from({ length }, (_, i) => contentCallback(i))
}

/**
 * Checks if a string contains Unicode emojis.
 *
 * @param emoji - The string to check.
 * @returns A boolean indicating if the string contains Unicode emojis.
 *
 * @example
 * ```ts
 * const hasEmoji = isUnicodeEmoji('Hello World 😊');
 * console.log(hasEmoji); // Output: true
 * ```
 */
export const isUnicodeEmoji = (emoji: string) => {
  return !!emoji?.match(/(\p{Emoji}|\p{Extended_Pictographic})/gu)
}

/**
 * Performs a case-insensitive search to check if the `query` exists within the `source`.
 *
 * - Handles strings, numbers, and arrays (including nested arrays) of strings/numbers.
 * - Treats `undefined` as an empty string.
 *
 * @param source - The value to search within. Can be:
 *   - A string or number.
 *   - A single-level or nested array of strings/numbers.
 * @param query - The value to search for. Treated as an empty string if `undefined`.
 * @returns `true` if the `query` is found within the `source` (case-insensitively), otherwise `false`.
 *
 * @example
 * ```typescript
 * // Single string or number search
 * searchCompare("Hello World", "world"); // true
 * searchCompare(12345, "234"); // true
 *
 * // Array search
 * searchCompare(["apple", "banana", "cherry"], "Banana"); // true
 * searchCompare([123, 456, 789], "456"); // true
 *
 * // Nested array search
 * searchCompare(["apple", ["banana", ["cherry"]]], "cherry"); // true
 * searchCompare([123, [456, [789]]], "456"); // true
 *
 * // Handling undefined
 * searchCompare(undefined, "test"); // false
 * searchCompare("test", undefined); // true
 * ```
 */
export const searchCompare = (
  source?: NestedArray<string | number | undefined>,
  query?: string,
  onMatch?: (source: string | number | undefined) => void,
): boolean => {
  if (ncIsArray(source)) {
    return source.some((item) => searchCompare(item, query, onMatch))
  }

  const isMatch = (source || '')
    .toString()
    .toLowerCase()
    .includes((query || '').toLowerCase())

  if (isMatch && onMatch) {
    onMatch(source)
  }

  return isMatch
}

/**
 * Filters options for an Ant Design Select component based on an input value.
 *
 * @param inputValue - The input value to filter against.
 * @param option - The option to evaluate for filtering.
 * @param searchKey - The key(s) in the option object to compare against the input value. Defaults to 'key'.
 * @returns `true` if the option matches the input value, otherwise `false`.
 */
export const antSelectFilterOption = (
  inputValue: string,
  option?: DefaultOptionType | NcListItemType,
  searchKey: keyof DefaultOptionType | keyof NcListItemType | (keyof NcListItemType)[] | (keyof DefaultOptionType)[] = 'key',
) => {
  if (!option) return false

  const optionValue = ncIsArray(searchKey) ? searchKey.map((key) => option[key]) : [option[searchKey]]

  return searchCompare(optionValue, inputValue)
}

/**
 * Extracts the name from an email address.
 *
 * @param email - The email address to extract the name from.
 * @returns The name extracted from the email address.
 *
 * @example
 * ```typescript
 * const name = extractNameFromEmail('john.doe@example.com');
 * console.log(name); // Output: 'john.doe'
 * ```
 */
export const extractNameFromEmail = (email?: string) => {
  if (!email) return ''

  return email?.slice(0, email.indexOf('@')) ?? ''
}

/**
 * Wait for a condition to be truthy
 * @param conditionFn - Function that returns the condition to check
 * @param interval - Polling interval in milliseconds (default: 100)
 * @returns Promise that resolves when condition becomes truthy
 */
export function waitForCondition(conditionFn: () => unknown, interval: number = 100): Promise<void> {
  return new Promise((resolve) => {
    const check = (): void => {
      if (conditionFn()) {
        resolve()
      } else {
        setTimeout(check, interval)
      }
    }
    check()
  })
}

export const pollUntil = <T>(conditionFn: () => T | null | undefined | false, interval: number = 100): Promise<T> => {
  return new Promise((resolve, reject) => {
    const check = () => {
      try {
        const result = conditionFn()
        if (result) {
          resolve(result)
        } else {
          setTimeout(check, interval)
        }
      } catch (error) {
        reject(error)
      }
    }

    check()
  })
}

export const getDraggableAutoScrollOptions = (
  params: Partial<AutoScrollOptions & { direction: SortableOptions['direction'] }> = {},
): Partial<AutoScrollOptions & { direction: SortableOptions['direction'] }> => {
  return {
    /**
     * scroll property is used to enable auto scroll plugin
     */
    scroll: true,
    /**
     * force the autoscroll fallback to kick in
     * if this value is false then updated `scrollSensitivity` will not work
     */
    forceAutoScrollFallback: true,
    /**
     * px, how near the mouse must be to an edge to start scrolling.
     */
    scrollSensitivity: 50,
    direction: 'vertical',
    ...params,
  }
}
